﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using TinyEngine.Core.Entities;
using TinyEngine.Core.Parsers;
using TinyEngine.Core.Services;
using TinyEngine.Core.Renderer;

namespace TinyEngine.Core.Camera
{
    [TinyEngine.Core.Attributes.EntityParser(typeof(Basic2DCameraParser))]
    public class Basic2DCamera : NullEntity, ICamera, IDebugRenderable
    {
        public Rectangle MovementBounds { get; set; }
        public Rectangle Bounds { get; set; }

        public Basic2DCamera()
        {
            this.Position = Vector3.Zero;
            this.Pivot = Vector2.Zero;
            this.Zoom = 1;
        }

        private Vector3 position;

        public Vector3 Position
        {
            get { return position; }
            set
            {
                position = value;
                CheckBounds();
                dirty = true;
                if (TinyGame.IsInEditor)
                    UpdateTransform();
            }
        }

        private void CheckBounds()
        {
            if (MovementBounds.Width == 0 && MovementBounds.Height == 0)
            {
                dirty = true;
            }
            else
            {
                if (MovementBounds.X + Pivot.X > position.X)
                {
                    position.X = MovementBounds.X + Pivot.X;
                    dirty = true;
                }
                if (MovementBounds.Right + Pivot.X < position.X)
                {
                    position.X = MovementBounds.Right + Pivot.X;
                    dirty = true;
                }

                if (MovementBounds.Y + Pivot.Y > position.Y)
                {
                    position.Y = MovementBounds.Y + Pivot.Y;
                    dirty = true;
                }
                if (MovementBounds.Bottom - Pivot.Y < position.Y)
                {
                    position.Y = MovementBounds.Bottom - Pivot.Y;
                    dirty = true;
                }
            }
            int x = (int)(position.X - Pivot.X);
            int y = (int)(position.Y - Pivot.Y);
            Bounds = new Rectangle(
                x,
                y,
                x + ViewPortSize.X,
                y + viewportSize.Y);

        }

        public Vector2 Pivot { get; set; }

        public float Zoom { get; set; }
        
        public float Rotation { get; set; }
        public Matrix Transform { get; set; }
        private Point viewportSize = Point.Zero;

        public override void Register(Scene.TinyScene owner)
        {
            base.Register(owner);
        }

        public Point ViewPortSize
        {
            get { return viewportSize; }

            set
            {
                viewportSize = value;
                CenterPivot();
            }
        }

        private void CenterPivot()
        {
            this.Pivot = new Vector2(this.viewportSize.X / 2, this.viewportSize.Y / 2);
        }

        bool dirty = false;
        
        public override void Update(float elapsed)
        {
            base.Update(elapsed);
            if (dirty)
                UpdateTransform();
        }

        public void UpdateTransform()
        {
            Vector2 offSet = Vector2.Zero;
            Vector2 camPos = Vector2.Zero;
            Vector2 transformedZoom = new Vector2(Zoom);
            transformedZoom.X = 1 + (Zoom - 1);
            transformedZoom.Y = 1 + (Zoom - 1);

            
            offSet.X = this.Pivot.X;
            offSet.Y = this.Pivot.Y;
            
            camPos.X = position.X;
            camPos.Y = position.Y;

            Transform = Matrix.CreateTranslation(-camPos.X, -camPos.Y, 0) *
                Matrix.CreateScale(transformedZoom.X, transformedZoom.Y, 1.0f) *
                Matrix.CreateRotationZ(Rotation) *
                Matrix.CreateTranslation(offSet.X, offSet.Y, 0);
        }
        public float RotationY { get; set; }
        public float RotationX { get; set; }
        public void Move(float x, int y)
        {
            this.position.X += x;
            this.position.Y += y;
            dirty = true;
        }

        public override void DrawDebug(float elapsed)
        {
            var r = ServiceManager.GetService<PrimitiveBatchService>();
            var rect = new Rectangle((int)(this.position.X - (ViewPortSize.X/2)),
                (int)(this.position.Y - (viewportSize.Y/2)),
                viewportSize.X,viewportSize.Y);
            rect.Inflate(12, 12);
            rect.Offset(-6, -6);
            r.Batch.DrawBox(rect, Color.Blue);
        }
    }

    public class BasicCamera : ICamera
    {
        public Rectangle MovementBounds { get; set; }
        public Rectangle Bounds { get; set; }

        public BasicCamera()
        {
            this.Position = Vector3.Zero;
            this.Pivot = Vector2.Zero;
            this.Zoom = 1;
        }

        private Vector3 position;

        public Vector3 Position
        {
            get { return position; }
            set
            {
                position = value;
                CheckBounds();
                dirty = true;
                
                if (TinyGame.IsInEditor)
                    UpdateTransform();
            }
        }

        private void CheckBounds()
        {

            if (MovementBounds.Width == 0 && MovementBounds.Height == 0)
            {
                dirty = true;
            }
            else
            {

                if (MovementBounds.X + Pivot.X > position.X)
                {
                    position.X = MovementBounds.X + Pivot.X;
                    dirty = true;
                }
                if (MovementBounds.Right + Pivot.X < position.X)
                {
                    position.X = MovementBounds.Right + Pivot.X;
                    dirty = true;
                }
            }
            int x = (int)(position.X - Pivot.X);
            int y = (int)(position.Y - Pivot.Y);
            Bounds = new Rectangle(
                x,
                y,
                x + ViewPortSize.X,
                y + viewportSize.Y);

        }
        
        public Vector2 Pivot { get; set; }

        public float Zoom { get; set; }
        public string Name { get; set; }
        public float Rotation { get; set; }
        public Matrix Transform { get; set; }
        private Point viewportSize = Point.Zero;
        public Point ViewPortSize 
        {
            get { return viewportSize; }

            set 
            { 
                viewportSize = value;
                CenterPivot();
            }
        }

        private void CenterPivot()
        {
            this.Pivot = new Vector2(this.viewportSize.X / 2, this.viewportSize.Y / 2);
        }
        
        bool dirty = false;
        public void Update(float elapsed)
        {
            if (dirty)
                UpdateTransform();
        }

        public void UpdateTransform()
        {
            Vector2 offSet = Vector2.Zero;
            Vector2 camPos = Vector2.Zero;
            Vector2 transformedZoom = new Vector2(Zoom);
            transformedZoom.X = 1 + (Zoom - 1);
            transformedZoom.Y = 1 + (Zoom - 1);

            //if (this.IsPivotRelative == true)
            //{
            //    offSet.X = this.Pivot.X * DrawingManager.ViewPortSize.X;
            //    offSet.Y = this.Pivot.Y * DrawingManager.ViewPortSize.Y;
            //}
            //else
            {
                offSet.X = this.Pivot.X;
                offSet.Y = this.Pivot.Y;
            }
            camPos.X = position.X;
            camPos.Y = position.Y;
            
            Transform = Matrix.CreateTranslation(-camPos.X, -camPos.Y, 0) *
                Matrix.CreateScale(transformedZoom.X, transformedZoom.Y, 1.0f) *
                Matrix.CreateRotationZ(Rotation) *
                Matrix.CreateTranslation(offSet.X, offSet.Y, 0);
        }


        public void Move(float x, int y)
        {
            this.position.X += x;
            this.position.Y += y;
            dirty = true;
        }
    }
}
